
// ===============================================================================================================
// -*- C++ -*-
//
// Sprite.cpp - An animated image sequence (sprite).
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#include <Sprite.hpp>

Sprite::Sprite(void)
: frameList(0), currentFrame(0), numFrames(0), timeCounter(0)
{
	// Initialize everything to zero
}

Sprite::Sprite(Image * const imageList[], int count, bool cloneImages, float perFrameDelay)
: frameList(0), currentFrame(0), numFrames(0), timeCounter(0)
{
	if ((imageList != 0) && (count > 0))
	{
		for (int i = 0; i < count; ++i)
		{
			AddImage(imageList[i], cloneImages, perFrameDelay);
		}

		currentFrame = frameList;
	}
}

Sprite * Sprite::Create(Image * const imageList[], int count, bool cloneImages, float perFrameDelay)
{
	try {
		return (new Sprite(imageList, count, cloneImages, perFrameDelay));
	}
	catch (...) {
		LOG_ERROR("Failed to create sprite from image list! Possible memory allocation failure...");
		return (0);
	}
}

bool Sprite::AddImage(const Image * img, bool cloneImage, float frameDelay)
{
	if (!img)
	{
		LOG_ERROR("Null image pointer!");
		return (false);
	}

	try {

		// Allocate a new frame:

		Frame * tmp = new Frame;

		if (cloneImage)
		{
			tmp->image = img->Clone();

			if (tmp->image->Fail())
			{
				tmp->image->Release();
				tmp->image = 0;

				// Frame no good, delete it:
				delete tmp;
				tmp = 0;

				return (false);
			}
		}
		else
		{
			tmp->image = const_cast<Image *>(img);
			tmp->image->AddRef(); // No clone copy, just AddRef.
		}

		if (frameList != 0)
		{
			Frame * save = 0;
			Frame * head = frameList;

			// Navigate to the end of the list and add a new node.
			// I know I'am doing it completly wrong but C++ made me so lazy
			// that I don't even remember how to make a decent linked list :(

			for (/**/; /**/; head = head->next)
			{
				if (head != 0)
				{
					save = head;
				}
				else
				{
					break;
				}
			}

			save->next = tmp;
			tmp->next = 0;
		}
		else
		{
			frameList = tmp;
			tmp->next = 0;
		}

		tmp->delay = frameDelay;

		++numFrames;
	}
	catch (...) {

		LOG_ERROR("Failed to create a sprite frame! Possible memory allocation failure...");
		return (false);
	}

	return (true);
}

void Sprite::Clear(void)
{
	// Free all memory chunks:

	Frame * tmp = frameList;

	while (frameList != 0)
	{
		tmp = frameList->next;

		frameList->image->Release();
		delete frameList;

		frameList = tmp;
	}

	currentFrame = 0;
	numFrames    = 0;
	timeCounter  = 0;
}

void Sprite::SetPerFrameDelay(float frameDelay)
{
	// Set the time delay for all frames:

	for (Frame * tmp = frameList; tmp != 0; tmp = tmp->next)
	{
		tmp->delay = frameDelay;
	}
}

Image * Sprite::GetFrame(float elapsedTime) const
{
	if (currentFrame == 0)
	{
		if ((currentFrame = frameList) == 0)
		{
			// Sprite never initialized!
			return (0);
		}
	}

	timeCounter += elapsedTime;

	if (timeCounter > currentFrame->delay)
	{
		currentFrame = currentFrame->next;

		if (currentFrame == 0)
		{
			currentFrame = frameList;
		}

		timeCounter = 0.0f;
	}

	return (currentFrame->image);
}

Image * Sprite::GetCurrentFrame(void) const
{
	return (currentFrame->image);
}

Image * Sprite::GetFrameSequence(float elapsedTime, int firstFrame, int lastFrame) const
{
	if (currentFrame == 0)
	{
		if ((currentFrame = frameList) == 0)
		{
			// Sprite never initialized!
			return (0);
		}
	}

	timeCounter += elapsedTime;

	if (timeCounter > currentFrame->delay)
	{
		currentFrame = currentFrame->next;

		int n = 0;
		Frame * first = frameList;

		// Find the first frame:
		while (first && (n < firstFrame))
		{
			++n;
			first = first->next;
		}

		n = 0;
		Frame * last = frameList;

		// Find the last frame:
		while (last && (n <= lastFrame))
		{
			++n;
			last = last->next;
		}

		if (currentFrame == last)
		{
			currentFrame = first;
		}

		timeCounter = 0.0f;
	}

	return (currentFrame->image);
}

int Sprite::GetNumImages(void) const
{
	return (numFrames);
}

Sprite * Sprite::Clone(void) const
{
	try { 

		Sprite * s = new Sprite;

		for (Frame * tmp = this->frameList; tmp != 0; tmp = tmp->next)
		{
			s->AddImage(tmp->image, true, tmp->delay);
		}

		s->currentFrame = s->frameList;

		return (s);
	}
	catch (...) {

		LOG_ERROR("Failed to clone a sprite frame! Possible memory allocation failure...");
		return (0);
	}
}

unsigned long Sprite::AddRef(void) const
{
	return (++refCount);
}

unsigned long Sprite::Release(void) const
{
	if (--refCount == 0)
	{
		// Time to die...
		delete this;
		return (0);
	}

	return (refCount);
}

unsigned long Sprite::ReferenceCount(void) const
{
	return (refCount);
}

Sprite::~Sprite(void)
{
	Clear();
}